package ua.kharkov.khpi.tarasenko.bach.service;

import java.sql.Connection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import ua.kharkov.khpi.tarasenko.bach.analysis.prefixspan.Item;
import ua.kharkov.khpi.tarasenko.bach.analysis.prefixspan.Node;
import ua.kharkov.khpi.tarasenko.bach.analysis.prefixspan.PrefixSpanAlgorythm;
import ua.kharkov.khpi.tarasenko.bach.analysis.prefixspan.PseudoDbPS;
import ua.kharkov.khpi.tarasenko.bach.analysis.tnr.AlgoTNR;
import ua.kharkov.khpi.tarasenko.bach.analysis.tnr.PseudoDbTNR;
import ua.kharkov.khpi.tarasenko.bach.analysis.tnr.RedBlackTree;
import ua.kharkov.khpi.tarasenko.bach.analysis.tnr.RuleG;
import ua.kharkov.khpi.tarasenko.bach.core.EShopException;
import ua.kharkov.khpi.tarasenko.bach.core.db.ConnectionManager;
import ua.kharkov.khpi.tarasenko.bach.core.db.DBUtils;
import ua.kharkov.khpi.tarasenko.bach.core.db.EShopDataStoreException;
import ua.kharkov.khpi.tarasenko.bach.core.logging.TransactionType;
import ua.kharkov.khpi.tarasenko.bach.dao.core.assocrules.AssocRulesDAO;
import ua.kharkov.khpi.tarasenko.bach.dao.core.tree.TreeDAO;
import ua.kharkov.khpi.tarasenko.bach.dao.mysql.assocrules.AssocRulesDAOImpl;
import ua.kharkov.khpi.tarasenko.bach.dao.mysql.tree.TreeDAOImpl;

public class AnalysisService {
	private static final Logger LOG = Logger.getLogger(AnalysisService.class);

	public Node doNavigationRecsAnalysis(PseudoDbPS db, double minSupPerc)
			throws EShopException {
		deleteTreeFromDB();

		PrefixSpanAlgorythm ps = new PrefixSpanAlgorythm();
		int minSupNum = (int) (((double) db.size) * minSupPerc);

		Node rootNode = ps.analyze(db, minSupNum);
		LOG.info("Navigation recommendations' analyse finished");

		saveTreeToDB(rootNode);
		LOG.info("Tree saving to DB finished");

		return rootNode;
	}

	public void doCartRecsAnalysis(PseudoDbTNR database, double minConfidence,
			int k) throws EShopException {

		AlgoTNR tnr = new AlgoTNR();

		int delta = (int) ((double) k * 0.05);
		RedBlackTree<RuleG> rules = tnr.runAlgorithm(k, minConfidence,
				database, delta);
		LOG.info("Cart recommendations' analyse finished");
		deleteRules();
		LOG.info("All rules are deleted fom DB");
		saveRules(rules);
		LOG.info("All rules are saved fom DB");
	}

	private void saveTreeToDB(Node rootNode) throws EShopException {

		// root node does not have item in it and parent
		int parentId = 1;

		Node rootStubNode = new Node();

		Item rootItemStub = Item.getItem("0");
		rootItemStub.setTransactionType(TransactionType.VIEW);

		rootStubNode.setItem(rootItemStub);
		rootStubNode.setSupport(0);

		Connection conn = ConnectionManager.getConnection();
		TreeDAO treeDAO = new TreeDAOImpl(conn);
		int nodeId = treeDAO.saveNode(rootStubNode);
		treeDAO.savePath(nodeId, parentId);

		saveTreeToDB(rootNode, parentId);
		DBUtils.commitAndClose(conn);
	}

	private void saveTreeToDB(Node rootNode, int parentId)
			throws EShopException {

		Connection conn = ConnectionManager.getConnection();

		TreeDAO treeDAO = new TreeDAOImpl(conn);
		for (Node node : rootNode.getChildrenNodes()) {
			int nodeId = treeDAO.saveNode(node);
			treeDAO.savePath(nodeId, parentId);
			if (node.hasChildren()) {
				saveTreeToDB(node, nodeId);
			}

		}
		DBUtils.commitAndClose(conn);
	}

	public Node getTreeFromDB() throws EShopException {
		Connection conn = ConnectionManager.getConnection();

		TreeDAO treeDAO = new TreeDAOImpl(conn);

		Map<Integer, Node> nodes = treeDAO.getAllNodes();

		// Get root node
		Node rootNode = nodes.get(1);

		// Get root node's direct childs
		rootNode.setChildren(getChildren(nodes, 1, treeDAO));

		DBUtils.close(conn);
		return rootNode;
	}

	public void deleteTreeFromDB() throws EShopException {
		Connection conn = ConnectionManager.getConnection();

		TreeDAO treeDAO = new TreeDAOImpl(conn);
		treeDAO.deleteTree();
		DBUtils.commitAndClose(conn);
	}

	private Map<String, Node> getChildren(Map<Integer, Node> nodes,
			int parentId, TreeDAO treeDAO) throws EShopDataStoreException {
		Map<String, Node> children = new HashMap<String, Node>();
		List<Integer> childIds = treeDAO.getDirectChildNodesId(parentId);
		for (Integer childId : childIds) {
			Node child = nodes.get(childId);
			child.setChildren(getChildren(nodes, childId, treeDAO));
			children.put(child.getItemS(), child);
		}
		return children;
	}

	public boolean isTreeInDb() throws EShopException {
		Connection conn = ConnectionManager.getConnection();
		TreeDAO treeDAO = new TreeDAOImpl(conn);
		boolean isTreeInDB = treeDAO.isTreeInDb();
		DBUtils.close(conn);
		return isTreeInDB;
	}

	public void saveRules(RedBlackTree<RuleG> rules) throws EShopException {
		Connection conn = ConnectionManager.getConnection();

		AssocRulesDAO rulesDAO = new AssocRulesDAOImpl(conn);
		Iterator<RuleG> it = rules.iterator();
		while (it.hasNext()) {
			rulesDAO.saveRule(it.next());
		}
		DBUtils.commitAndClose(conn);
	}

	public void deleteRules() throws EShopException {
		Connection conn = ConnectionManager.getConnection();

		AssocRulesDAO rulesDAO = new AssocRulesDAOImpl(conn);
		rulesDAO.deleteAllRules();
		DBUtils.commitAndClose(conn);
	}
}